package liquidfs.core;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

import liquidfs.*;
import liquidfs.data.*;
import liquidfs.data.fs.*;

public class HandleManager {

   private AtomicLong nextID = new AtomicLong(0);
   private Map<Long, Handle> handlesByID = new ConcurrentHashMap<Long, Handle>();
   private Map<String, List<Handle>> handlesByPath = new ConcurrentHashMap<String, List<Handle>>();
   
   private LiquidFileSystem fs;
   
   public HandleManager (Application app) {
      this.fs = app.getFileSystem();
   }
   
   public Handle createHandle (OpenMode openMode, SharingMode sharingMode, String path) {
      
      // Get the next available ID
      long id = nextID.getAndIncrement();
      LiquidObject object;
      
      // Get any existing handles to this same path
      List<Handle> existing = handlesByPath.get(path);
      
      // If the list was null, create an empty list
      if (existing == null) {
         existing = new ArrayList<Handle>(1);
         handlesByPath.put(path, existing);
      }
      
      // If there are any handles previously existing
      if (existing.size() > 0) {
         
         // Check their sharing modes to see whether we can allow this
         for (Handle handle : existing) {
            // If it is not permitted, deny the request
            if (!handle.getShareMode().permits(openMode)) {
               return null;
            }
         }
         
         // Use the previously created object
         object = existing.get(0).getObject();
      }
      else {
         // Get a new object if one hasn't been created yet
         object = fs.getPath(path);
      }
      
      Handle handle = new Handle(id, object, sharingMode, openMode);
      existing.add(handle);
      
      handlesByID.put(id, handle);
      
      return handle;
   }
   
   public Handle getHandle (long id) {
      return handlesByID.get(id);
   }
   
   public void destroyHandle (long id) {
      
      // Remove the handle
      Handle handle = handlesByID.remove(id);
      
      // Check if it's open elsewhere
      List<Handle> existing = handlesByPath.get(handle.getObject().getPath());
      
      // If there aren't any open handles, make sure this object is closed
      if (existing != null && existing.size() == 0 && !handle.getObject().isDirectory()) {
         
         LiquidFile file = handle.getObject().asFile();
         if (file.isOpen())
            file.close();
      }
   }
}
